﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;

namespace Chat_Client_TCP
{
    public partial class MainForm : Form
    {
        #region Глобальные переменные
        private string username;
        private IPAddress ip_server;
        private int port_server;

        public bool connected;

        public TcpClient myClient;         //Основной класс для подключения к серверу и общения с ним
        private StreamWriter sw;            //Поток отправления сообщений
        private StreamReader sr;            //Поток чтения сообщений от сервера
        private Thread thrMessaging;        //Поток обработки сообщений от сервера
        private Thread thrSend;             //Поток отправки сообщения на сервер

        //колбеки для обработки функций из других потоков
        private delegate void UpdateLogCallback(string strMessage);
        private delegate void UpdateUserListCallback(List<string> userlist);
        private delegate void CloseConnectionCallback(string strReason);
        #endregion                      

        public MainForm()
        {
            CallBackMyToConnect.callbackEventHandler = new CallBackMyToConnect.callbackEvent(this.Reload);
            InitializeComponent();
        }
        void Reload(bool param)
        {
            //param - нажата ли кнопка connect в окне hello
            if (param)
                start_connect();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            myClient = new TcpClient();
            //Показывает окно с настройками
            this.Show();
            showLoginForm();
        }

        public void start_connect()
        {
            connected = false;
            username = Properties.Settings.Default.userName;
            ip_server = IPAddress.Parse(Properties.Settings.Default.IP_server);
            port_server = Properties.Settings.Default.Port_server;
            myClient = new TcpClient();
            //соединение с сервером
            try
            {
                myClient.Connect(ip_server, port_server);
                connected = true;
                дисконнектToolStripMenuItem.Enabled = true;
                логинToolStripMenuItem.Enabled = false;
            }
            catch (Exception e)
            {
                //в случае ошибки подключения выводим message box
                MessageBox.Show("Ошибка подключения");
            }
            //если подключение прошло успешно
            if (connected)
            {
                try
                {
                    //отправляем серверу имя пользователя
                    sendMessage(username);

                    //устанавливаем режим ожидания сообщений от сервера
                    thrMessaging = new Thread(new ThreadStart(ReceiveMessages));
                    thrMessaging.Start();
                }
                catch (Exception e)
                {
                    //в случае ошибки отправки
                    MessageBox.Show(e.ToString());
                }
            }
            
        }

        public void showLoginForm()
        {
            HelloForm frm = new HelloForm();
            frm.Owner = this;
            frm.ShowDialog();
        }

        //ожидание сообщений от сервера
        private void ReceiveMessages()
        {
            //ожидание ответа от сервера
            sr = new StreamReader(myClient.GetStream());
            string ConResponse = sr.ReadLine();
            // если первый символ сообщения - 1, то соединение с сервером прошло успешно
            if (ConResponse[0] == '1')
            {
                // пишем об удачном подключении в лог
                this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { "Соеднинение установлено!" });
            }
            else // если подключение не прошло
            {
                string Reason = "Подключение не установлено\r\nПричина: ";
                Reason += ConResponse.Substring(2, ConResponse.Length - 2);
                // закрываем соединение
                this.Invoke(new CloseConnectionCallback(this.CloseConnection), new object[] {Reason});
                
                return;
            }
            // пока подключение активно, читаем входящие строки с сервера
            try
            {
                while ((connected && (ConResponse = sr.ReadLine()) != ""))
                {
                    // отображаем сообщения в окне
                    bool systemComand = false;
                    if (ConResponse.Length >= 12)
                    {
                        if (ConResponse.Substring(0, 12) == "\\disconnect|")
                        {
                            string reason = ConResponse.Substring(12, ConResponse.Length - 12);
                            this.Invoke(new CloseConnectionCallback(this.CloseConnection), new object[] { reason });
                            systemComand = true;
                        }
                    }
                    if (ConResponse.Length >= 10)
                    {
                        if (ConResponse.Substring(0, 10) == "\\userlist|")
                        {
                            List<string> userlist = ConResponse.Substring(10, ConResponse.Length - 10).Split('|').ToList();
                            this.Invoke(new UpdateUserListCallback(this.UpdateUserList), userlist);
                            systemComand = true;
                        }
                    }
                    if (!systemComand)
                    {
                        this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { ConResponse });
                    }
                }

            }
            catch (Exception e)
            {
                if (connected)
                    this.Invoke(new CloseConnectionCallback(this.CloseConnection), new object[] { "\r\nДисконнект!\r\n" });
            }
        }

        //отправка сообщений на сервер
        private void sendMessage(string message)
        {
            if (connected)
            {
                try
                {
                    sw = new StreamWriter(myClient.GetStream());
                    sw.WriteLine(message);
                    sw.Flush();
                    message_textBox.Clear();
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.ToString());
                }
            }
            else
            {
                DialogResult result;
                result = MessageBox.Show("Соединение с сервером не установлено\nУстановить соединение сейчас?","Ошибка",MessageBoxButtons.YesNo);
                if (result.ToString() == "Yes")
                {
                    showLoginForm();
                }
            }
        }

        // Метод обновления richTextBox при получении сообщения
        private void UpdateLog(string message)
        {
            System.Media.SystemSound s = System.Media.SystemSounds.Asterisk;
            s.Play();

            string time = DateTime.Now.ToString("[HH:mm:ss] ");
            chat_RichTextBox.AppendText(time + message + "\r\n");
            StreamWriter log = new StreamWriter("log.txt", true);
            log.WriteLine(message);
            log.Close();
        }

        private void UpdateUserList(List<string> userlist)
        {
            userList_ListBox.Items.Clear();
            for (int i = 0; i < userlist.Count; i++)
            {
                userList_ListBox.Items.Add(userlist[i]);
            }
        }

        // закрывает текущее соединение
        private void CloseConnection(string Reason)
        {
            // Отображаем причину дисконнекта в окне
            chat_RichTextBox.AppendText(Reason + "\r\n");
            //Отправляем серверу сообщение о том, что мы закрываем соединение
            if (connected)
            {
                if (Reason!= "\r\nДисконнект!\r\n")
                    sendMessage("\\disconnect");
                connected = false;
                sw.Close();
                sr.Close();
                myClient.Close();
                дисконнектToolStripMenuItem.Enabled = false;
                логинToolStripMenuItem.Enabled = true;

                userList_ListBox.Items.Clear();
            }
        }

        private void message_textBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)13)
            {
                sendMessage(message_textBox.Text);
            }
        }

        //кнопка отправить
        private void send_Button_Click(object sender, EventArgs e)
        {
            sendMessage(message_textBox.Text);
        }

        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (connected)
                this.Invoke(new CloseConnectionCallback(this.CloseConnection), new object[]{"Приложение закрылось"});
        }

        //пункты меню
        private void выходToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
        private void логинToolStripMenuItem_Click(object sender, EventArgs e)
        {
            HelloForm frm = new HelloForm();
            frm.Owner = this;
            frm.Show();
        }
        private void дисконнектToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (connected)
                this.Invoke(new CloseConnectionCallback(this.CloseConnection), "Нет соединения");
            else
                MessageBox.Show("Нет соединения");
        }

        //автопрокрутка окно сообщений
        private void chat_RichTextBox_TextChanged(object sender, EventArgs e)
        {
            chat_RichTextBox.SelectionStart = chat_RichTextBox.Text.Length;
            chat_RichTextBox.ScrollToCaret();
        }

        
    }
}
